.intel_syntax noprefix
.globl	main

main:
    # function prologue
	endbr64
	push rbp
	mov	 rbp, rsp
	sub	 rsp, 16

    # edi -> argc
	mov	 dword ptr -4[rbp], edi
    # rsi -> argv
	mov	 qword ptr -16[rbp], rsi
    # 将 argv 放入 rax
	mov	 rax, qword ptr -16[rbp]
    # 将argv[1] 放入 rax (指针占用8个字节)
	add	 rax, 8
    # argv 是字符串数组, 这里将 &argv[1] 放入 rax
	mov	 rax, qword ptr [rax]
    # copy 的第一个参数
	mov	 rdi, rax
    # 1. 将 rip 压栈 (即 leave 的地址)
    # 2. 跳转到 copy 执行
	call copy
	leave
	ret

copy:
	endbr64
    # 这个部分叫做 "function prologue", 几乎每个函数开头都有类似的压栈代码.
    # 这并不是 x86-64 必须的, 但是一种 convention, 编译器会自动添加.
    # 1. 保存 rbp, 即 caller frame 的起始位置.
	push rbp
    # 2. 将当前函数 frame 起始位置赋给 rbp.
	mov	 rbp, rsp
    # 3. 在栈中开辟空间, 用于存放局部变量
    # 在本例中, 需要8字节用于字符串指针(?), 16字节用于缓冲区.
    # 由于栈需要16字节对齐, 这里共需开辟32字节空间
    sub	 rsp, 32
    # 此时 copy frame 形状如下 (每行8字节):
    #
    #     .-------0x00-------.
    #     :                  :
    #     |                  | <- %rsp
    #     |     padding      |
    #     |       str        |
    #     |  buffer[ 0-7  ]  |
    #     |  buffer[ 8-15 ]  | <- %rbp
    #     | [ main's  %rbp ] |
    #     | [return address] |
    #     |------------------|
    #     :   main's frame   :
    #     `------------------`
    #
    # 或横过来看:
    #
    #   内存低地址                                           内存高地址
    #          str          buffer         sbp       ret
    #       [        ][ 0           15 ][        ][        ] main...
    #   栈顶                                                 栈底

	# x86-64 calling convention 规定函数第一个参数在 rdi 中
    mov -24[rbp], rdi

    # 现在准备好调用 strcpy, 需要两个参数, target 和 destination
    # 我们需要分别将其放在 rdi 和 rsi 中
    # 1. 将 &buffer[0] 作为 target 放入 rdi
    # (lea 会将地址计算出来)
    lea rdi, -16[rbp]

    # 2. 将字符串指针作为 dest 放入 rsi
    # 这里可以不借助栈来传递字符串指针而是直接将 rdi 传给 rsi
    mov rsi, -24[rbp]
    call    strcpy

    # strcpy 返回后, 从这里开始继续执行, 为了打印字符串, 将
    # &buffer[0] 作为参数传递给 puts 函数.
    lea rdi, -16[rbp]
    call    puts

    # 最终, 在返回前需要恢复 rsp 和 rbp
    # 关于 leave 指令可见 https://www.felixcloutier.com/x86/leave 
    leave
    # ret 将返回地址 pop并跳转
    ret
